package com.coderworm.tcp;


import java.io.*;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.zip.CRC32;

/**
 * 处理单个设备的RTCM文件接收（实现Runnable，支持线程池）
 */
public class RtcmFileHandler implements Runnable {
    private final Socket clientSocket;
    // RTCM文件保存路径（建议按日期分文件夹，避免文件名冲突）
    private static final String RTCM_SAVE_DIR = "D:/rtcm_files/";

    public RtcmFileHandler(Socket clientSocket) {
        this.clientSocket = clientSocket;
    }

    @Override
    public void run() {
        // 自动关闭流和socket（try-with-resources语法，Java 7+）
        try (InputStream in = clientSocket.getInputStream();
             BufferedInputStream bis = new BufferedInputStream(in); // 缓冲流，提升读取效率
             OutputStream out = clientSocket.getOutputStream();
             BufferedOutputStream bos = new BufferedOutputStream(out)) {

            // -------------------------- 1. 解析协议元数据 --------------------------
            // 1.1 读取文件名长度（2字节，无符号短整型）
            byte[] fileNameLenBytes = new byte[2];
            int readLen = bis.read(fileNameLenBytes); // 读取2字节，阻塞直到收到数据
            if (readLen != 2) {
                sendResponse(bos, "ERROR: 文件名长度读取失败");
                return;
            }
            int fileNameLen = ByteBuffer.wrap(fileNameLenBytes).order(java.nio.ByteOrder.BIG_ENDIAN).getShort() & 0xFFFF; // 转为无符号短整型

            // 1.2 读取文件名（fileNameLen字节，UTF-8编码）
            byte[] fileNameBytes = new byte[fileNameLen];
            readLen = bis.read(fileNameBytes);
            if (readLen != fileNameLen) {
                sendResponse(bos, "ERROR: 文件名读取失败");
                return;
            }
            String fileName = new String(fileNameBytes, StandardCharsets.UTF_8);
            System.out.println("接收文件名：" + fileName);

            // 1.3 读取文件总大小（8字节，无符号长整型）
            byte[] fileSizeBytes = new byte[8];
            readLen = bis.read(fileSizeBytes);
            if (readLen != 8) {
                sendResponse(bos, "ERROR: 文件大小读取失败");
                return;
            }
            long fileTotalSize = ByteBuffer.wrap(fileSizeBytes).order(java.nio.ByteOrder.BIG_ENDIAN).getLong();
            System.out.println("接收文件大小：" + fileTotalSize + " 字节");

            // 1.4 读取CRC32校验码（4字节，可选，按约定）
            byte[] crcBytes = new byte[4];
            readLen = bis.read(crcBytes);
            if (readLen != 4) {
                sendResponse(bos, "ERROR: CRC校验码读取失败");
                return;
            }
            long expectedCrc = ByteBuffer.wrap(crcBytes).order(java.nio.ByteOrder.BIG_ENDIAN).getInt() & 0xFFFFFFFFL; // 转为无符号整型


            // -------------------------- 2. 接收并保存RTCM文件内容 --------------------------
            // 2.1 创建保存目录（不存在则创建）
            File saveDir = new File(RTCM_SAVE_DIR);
            if (!saveDir.exists()) {
                boolean mkdirSuccess = saveDir.mkdirs();
                if (!mkdirSuccess) {
                    sendResponse(bos, "ERROR: 保存目录创建失败");
                    return;
                }
            }
            File rtcmFile = new File(saveDir, fileName);

            // 2.2 接收文件内容并写入本地（用缓冲流，避免频繁IO）
            try (FileOutputStream fos = new FileOutputStream(rtcmFile);
                 BufferedOutputStream fileBos = new BufferedOutputStream(fos)) {

                byte[] buffer = new byte[4096]; // 缓冲区大小：4KB（平衡内存占用和效率）
                long receivedSize = 0; // 已接收字节数
                CRC32 crc32 = new CRC32(); // 计算实际接收数据的CRC，验证完整性

                // 循环读取，直到接收完所有字节（receivedSize == fileTotalSize）
                while (receivedSize < fileTotalSize) {
                    // 计算本次读取的最大字节数（避免缓冲区溢出）
                    int maxRead = (int) Math.min(buffer.length, fileTotalSize - receivedSize);
                    int readBytes = bis.read(buffer, 0, maxRead);

                    if (readBytes == -1) {
                        // 输入流关闭（设备断开连接），接收中断
                        sendResponse(bos, "ERROR: 设备断开连接，文件接收中断");
                        rtcmFile.delete(); // 删除不完整文件
                        return;
                    }

                    // 写入文件 + 更新已接收大小 + 累计CRC
                    fileBos.write(buffer, 0, readBytes);
                    receivedSize += readBytes;
                    crc32.update(buffer, 0, readBytes);

                    // 打印接收进度（可选，便于调试）
                    System.out.printf("接收进度：%.2f%%（%d/%d 字节）%n",
                            (receivedSize * 100.0) / fileTotalSize, receivedSize, fileTotalSize);
                }

                // 强制刷新缓冲区，确保数据写入磁盘（避免缓冲导致文件不完整）
                fileBos.flush();


                // -------------------------- 3. 校验与响应 --------------------------
                // 3.1 校验CRC（确保文件未损坏）
                long actualCrc = crc32.getValue();
                if (actualCrc != expectedCrc) {
                    sendResponse(bos, "ERROR: 文件CRC校验失败（预期：" + expectedCrc + "，实际：" + actualCrc + "）");
                    rtcmFile.delete(); // 删除损坏文件
                    return;
                }

                // 3.2 发送成功响应（嵌入式端可根据响应判断是否重传）
                sendResponse(bos, "SUCCESS: 文件接收完成，保存路径：" + rtcmFile.getAbsolutePath());
                System.out.println("文件接收成功：" + rtcmFile.getAbsolutePath());

            } catch (Exception e) {
                sendResponse(bos, "ERROR: 文件写入失败：" + e.getMessage());
                rtcmFile.delete();
                e.printStackTrace();
            }

        } catch (Exception e) {
            System.err.println("设备处理异常：" + e.getMessage());
            e.printStackTrace();
        } finally {
            // 关闭socket（即使发生异常，也要确保连接关闭）
            try {
                if (!clientSocket.isClosed()) {
                    clientSocket.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            System.out.println("设备连接已关闭：" + clientSocket.getInetAddress().getHostAddress());
        }
    }

    /**
     * 向嵌入式设备发送响应（UTF-8编码，末尾加换行符，便于设备识别响应结束）
     */
    private void sendResponse(BufferedOutputStream bos, String response) throws IOException {
        byte[] responseBytes = (response + "\n").getBytes(StandardCharsets.UTF_8);
        bos.write(responseBytes);
        bos.flush(); // 立即发送，避免缓冲
        System.out.println("发送响应给设备：" + response);
    }
}